package com.jiangyifen.sipphone;

import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jiangyifen.sipphone.core.FileLogger;
import com.jiangyifen.sipphone.core.media.AbstractSoundManager;
import com.jiangyifen.sipphone.core.media.MediaManager;
import com.jiangyifen.sipphone.core.sip.RFC3261;
import com.jiangyifen.sipphone.core.sip.Utils;
import com.jiangyifen.sipphone.core.sip.core.useragent.SipListener;
import com.jiangyifen.sipphone.core.sip.core.useragent.UserAgent;
import com.jiangyifen.sipphone.core.sip.syntaxencoding.SipHeaderFieldName;
import com.jiangyifen.sipphone.core.sip.syntaxencoding.SipHeaderFieldValue;
import com.jiangyifen.sipphone.core.sip.syntaxencoding.SipHeaders;
import com.jiangyifen.sipphone.core.sip.syntaxencoding.SipUriSyntaxException;
import com.jiangyifen.sipphone.core.sip.transactionuser.Dialog;
import com.jiangyifen.sipphone.core.sip.transactionuser.DialogManager;
import com.jiangyifen.sipphone.core.sip.transport.SipRequest;
import com.jiangyifen.sipphone.core.sip.transport.SipResponse;

public class SipPhoneClientImpl extends AbstractSoundManager implements SipPhoneClient, SipListener {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private UserAgent userAgent;

	private SipRequest sipRequest;

	private SipPhoneConfig config;

	private Queue<byte[]> voiceDataQueue = new ConcurrentLinkedQueue<>();

	private static final byte[] blankData = new byte[320];


	public SipPhoneClientImpl(SipPhoneConfig config) throws IOException {

		this.config = config;

		userAgent = new UserAgent(this, config, new FileLogger(null), this);

		this.register();

	}

	// ////////// //
	// ISipClient //
	// ////////// //

	@Override
	public void register() {
		new Thread(() -> {
			try {
				userAgent.register();
			} catch (SipUriSyntaxException e) {
				logger.error(e.getMessage(), e);
			}
		}).start();
	}

	@Override
	public void unregister() {
		new Thread(() -> {
			try {
				userAgent.unregister();
			} catch (SipUriSyntaxException e) {
				logger.error(e.getMessage(), e);
			}
		}).start();
	}

	@Override
	public String call(final String phoneNumber) {
		String callId = "";
		try {
			sipRequest = userAgent.invite("sip:" + phoneNumber + "@" + config.getDomain(), null);
			callId = Utils.getMessageCallId(sipRequest);

			logger.debug("call(), callId={}", callId);
		} catch (SipUriSyntaxException e) {
			logger.error(e.getMessage(), e);
		}
		return callId;
	}

	@Override
	public void orignate(String exten, String gateway, String accessNumber) {
		// TODO Auto-generated method stub

	}
	
	@Override
	public void rejectCall(){
		new Thread(() -> {
			try {
				synchronized(userAgent){
					userAgent.rejectCall(sipRequest);
				}
				logger.debug("hangup()");
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}).start();
	}
	
	
	@Override
	public void hangup() {
		new Thread(() -> {
			try {
				synchronized(userAgent){
					userAgent.terminate(sipRequest);
				}
				logger.debug("hangup()");
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}).start();
	}

	@Override
	public void answer() {
		try {

			String callId = Utils.getMessageCallId(sipRequest);
			DialogManager dialogManager = userAgent.getDialogManager();
			Dialog dialog = dialogManager.getDialog(callId);
			userAgent.acceptCall(sipRequest, dialog);

			logger.debug("answer()");

		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	@Override
	public void dtmf(final char digit) {
		new Thread(() -> {
			try {
				MediaManager mediaManager = userAgent.getMediaManager();
				mediaManager.sendDtmf(digit);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}).start();
	}

	@Override
	public int say(byte[] voiceData) {

		if (voiceData != null) {
			try {
				voiceDataQueue.offer(voiceData);
				return voiceData.length;
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}

		return -1;
	}

	@Override
	public void clearVoiceDataQueue() {
		this.voiceDataQueue.clear();
	}

	// @Override
	// public void mute() {
	// this.mute = true;
	// }
	//
	// @Override
	// public void unmute() {
	// this.mute = false;
	// }

	// /////////// //
	// SipListener //
	// /////////// //

	@Override
	public void registering(SipRequest sipRequest) {
		System.out.println("===================");
		System.out.println("=== registering ===");
		System.out.println("===================");

		
	}

	@Override
	public void registerSuccessful(SipResponse sipResponse) {
		System.out.println("==========================");
		System.out.println("=== registerSuccessful ===");
		System.out.println("==========================");

	}

	@Override
	public void registerFailed(SipResponse sipResponse) {
		System.out.println("======================");
		System.out.println("=== registerFailed ===");
		System.out.println("======================");

	}

	@Override
	public void ringing(SipResponse sipResponse) {
		logger.debug("===============");
		logger.debug("=== ringing ===");
		logger.debug("===============");
		
		CallInfo callInfo = getCallInfo(sipRequest);
		callInfo.getCallId();
		callInfo.getPhoneNumber();
		
	}
	
	@Override
	public void incomingCall(SipRequest sipRequest, SipResponse provResponse) {
		logger.debug("====================");
		logger.debug("=== incomingCall ===");
		logger.debug("====================");

		try {

			this.sipRequest = sipRequest;

			// 确定来电号码
			CallInfo callInfo = getCallInfo(sipRequest);
			callInfo.getCallId();
			callInfo.getPhoneNumber();
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}

	}

	@Override
	public void calleePickup(SipResponse sipResponse) {
		logger.debug("====================");
		logger.debug("=== calleePickup ===");
		logger.debug("====================");
		
	}

	@Override
	public void remoteHangup(SipRequest sipRequest) {

		logger.debug("====================");
		logger.debug("=== remoteHangup ===");
		logger.debug("====================");

		CallInfo callInfo = getCallInfo(sipRequest);
		callInfo.getCallId();
		callInfo.getPhoneNumber();
		
	}
	
	@Override
	public void error(SipResponse sipResponse) {
		logger.error("=============");
		logger.error("=== error ===");
		logger.error("=============");

	}

	// //////////// //
	// SoundManager //
	// //////////// //

	@Override
	public void init() {
		logger.debug("init sound manager");
	}

	@Override
	public void close() {
		logger.debug("close sound manager");
		this.clearVoiceDataQueue();
	}

	/**
	 * 机器人说话
	 * 
	 * 软电话主线程会从这个readData()读取语音数据，并将得到的语音byte[]发送出去 <br>
	 * 这段数据会被发送给对端的sip uac, 相当于机器人在说话 用法：从你希望的语音源（比如，麦克风，或者一个本地的wav文件，或者一个http
	 * url）读取语音数据到buffer上，并返回。
	 */
	@Override
	public synchronized byte[] readData() {

		byte[] voiceData = voiceDataQueue.poll();

		if (voiceData == null) {
			return blankData;
		}
		
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			logger.debug("error! sleep(20) fail");
		}

		return voiceData;

	}

	/**
	 * 机器人听到的话
	 * 
	 * 软电话主线程会将收到的语音信号通过调用这个writeData给到这里
	 * 
	 * 收取对方ua的语音信号
	 * 
	 * 参数列表中的buffer是从rtp中收到的数据流，是电话对端那个人说话的声音，也就是机器人“听”到的声音
	 * 
	 * 在这里将语音送去做asr处理
	 */
	@Override
	public void writeData(byte[] buffer, int offset, int length) {
		//TODO 在这里将语音送去做asr处理
		System.out.println("data recv, length="+buffer.length);
		
	}
	
	private CallInfo getCallInfo(SipRequest sipRequest){
		
		CallInfo callInfo = new CallInfo();

		SipHeaders sipHeaders = sipRequest.getSipHeaders();
		SipHeaderFieldName sipHeaderFieldName = new SipHeaderFieldName(RFC3261.HDR_FROM);
		SipHeaderFieldValue from = sipHeaders.get(sipHeaderFieldName);
		String fromValue = from.getValue();
		String phoneNumber = fromValue.substring(fromValue.indexOf("<sip:") + 5, fromValue.indexOf("@"));
		String callId = Utils.getMessageCallId(sipRequest);
		
		callInfo.setPhoneNumber(phoneNumber);
		callInfo.setCallId(callId);
		
		return callInfo;
	}

	private class CallInfo{
		private String phoneNumber;
		private String callId;

		public String getPhoneNumber() {
			return phoneNumber;
		}

		public void setPhoneNumber(String phoneNumber) {
			this.phoneNumber = phoneNumber;
		}

		public String getCallId() {
			return callId;
		}

		public void setCallId(String callId) {
			this.callId = callId;
		}
	}
}


